<?php
/**
 * +-------------------------------------------------------------
 * |Caption:
 * +-------------------------------------------------------------
 * |Author: Lenny
 * +-------------------------------------------------------------
 * |Email:  1685964109@qq.com
 * +-------------------------------------------------------------
 * |Date:   2024/6/18 下午3:31
 * +-------------------------------------------------------------
 */

namespace app\common\service;

use app\common\constants\AdminConstant;
use think\facade\Db;
use think\helper\Str;

/**
 * 权限验证服务
 */
class AuthService
{
    /**
     * 用户ID
     * @var null
     */
    protected $adminId = null;

    /**
     * 默认配置
     * @var array
     */
    protected $config = [
            'auth_on'       => true,        // 权限开关
            'sys_admin'     => 'sys_admin',    // 用户表
            'sys_auth'      => 'sys_auth',     // 权限表
            'sys_menu'      => 'sys_menu',     // 菜单表
            'sys_auth_menu' => 'sys_auth_menu',// 权限-节点表
    ];

    /**
     * 管理员信息
     * @var array|\think\Model|null
     */
    protected $adminInfo;

    /**
     * 所有节点信息
     * @var array
     */
    protected $nodeList;

    /**
     * 管理员所有授权节点
     * @var array
     */
    protected $adminNode;

    /***
     * 构造方法
     * AuthService constructor.
     * @param  null  $adminId
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function __construct($adminId = null)
    {
        $this->adminId   = $adminId;
        $this->adminInfo = $this->getAdminInfo();
        $this->nodeList  = $this->getNodeList();
        $this->adminNode = $this->getAdminNode();
    }

    /**
     * 获取管理员信息
     */
    public function getAdminInfo()
    {
        return Db::name($this->config['sys_admin'])
                 ->where('id', $this->adminId)
                 ->find();
    }

    /**
     * 获取所有菜单节点信息
     */
    public function getNodeList(): array
    {
        return Db::name($this->config['sys_menu'])
                 ->column('id,api,title,type', 'api');
    }

    /**
     * 获取管理员所有授权节点
     */
    public function getAdminNode(): array
    {
        $nodeList  = [];
        $adminInfo = Db::name($this->config['sys_admin'])
                       ->where(['id' => $this->adminId, 'status' => 1,])
                       ->find();
        if (!empty($adminInfo) && !empty($adminInfo['auth_ids'])) {
            // 获取当管理员所有授权菜单
            $buildAuthSql     = Db::name($this->config['sys_auth'])
                                  ->distinct(true)
                                  ->whereIn('id', $adminInfo['auth_ids'])
                                  ->field('id')
                                  ->buildSql(true);
            // 获取当管理员所有授权节点
            $buildAuthNodeSql = Db::name($this->config['sys_auth_menu'])
                                  ->distinct(true)
                                  ->where("auth_id IN {$buildAuthSql}")
                                  ->field('menu_id')
                                  ->buildSql(true);
            // 获取当管理员所有授权菜单下的所有节点
            $nodeList         = Db::name($this->config['sys_menu'])
                                  ->distinct(true)
                                  ->where("id IN {$buildAuthNodeSql}")
                                  ->column('href');
        }
        return $nodeList;
    }

    /**
     * 获取当前节点
     * @return string
     */
    public function getCurrentNode(): string
    {
        return $this->parseNodeStr(request()->controller() . '/' . request()->action());
    }

    /**
     * 驼峰转下划线规则
     * @param  string  $node
     * @return string
     */
    public function parseNodeStr($node): string
    {
        $array = explode('/', $node);
        foreach ($array as $key => $val) {
            if ($key == 0) {
                $val = explode('.', $val);
                foreach ($val as &$vo) {
                    $vo = Str::snake(lcfirst($vo));
                }
                $val         = implode('.', $val);
                $array[$key] = $val;
            }
        }
        return implode('/', $array);
    }

    /**
     * 判断当前节点是否需要验证
     * @param $node
     * @return bool
     */
    public function checkNode($node = null): bool
    {
        // 判断是否为超级管理员
        if ($this->adminId == AdminConstant::SUPER_ADMIN_ID) {
            return true;
        }
        // 判断权限验证开关
        if ($this->config['auth_on'] == false) {
            return true;
        }
        // 判断是否需要获取当前节点
        if (empty($node)) {
            $node = $this->getCurrentNode();
        } else {
            $node = $this->parseNodeStr($node);
        }
        // 判断是否加入节点控制，优先获取缓存信息
        if (!isset($this->nodeList[$node])) {
            return false;
        }
        $nodeInfo = $this->nodeList[$node];
//        if ($nodeInfo['is_auth'] == 0) {
//            return true;
//        }
        // 用户验证，优先获取缓存信息
        if (empty($this->adminInfo) || $this->adminInfo['status'] != 1 || empty($this->adminInfo['auth_ids'])) {
            return false;
        }
        // 判断该节点是否允许访问
        if (in_array($node, $this->adminNode)) {
            return true;
        }
        return false;
    }

}